package main

import (
	"fmt"
	"net"
	"strings"
	"time"
)

type Client struct {
	C chan string	//网络地址
	Name string		//用户发送数据的通道
	Addr string		//用户名
}


func sendOnlineMsg(cli Client) {
	msg := cli.Name + " login"
	for _, addr := range onlineMap {
		addr.Conn.Write([]byte(msg))
	}
}


//在线用户map
var onlineMap map[string]Client

var message = make(chan string)

func Manager()  {
	onlineMap = make(map[string]Client)

	for {
		msg := <- message	//没有消息时，阻塞

		for _, cli := range onlineMap {
			cli.C<-msg
		}
	}
}

func WriteMsgToClient(cli Client, conn net.Conn){
	for msg := range cli.C {
		conn.Write([]byte(msg + "\n"))
	}
}

func MakeMsg(cli Client, msg string) (buf string){
	buf  = fmt.Sprintf("[%s]%s:%s", cli.Addr, cli.Name, msg)
	return
}


func HandleConn(conn net.Conn) {
	defer conn.Close()

	//获取客户端地址
	cliAddr := conn.RemoteAddr().String()

	//创建结构体
	cli := Client{make(chan string), cliAddr, cliAddr}

	onlineMap[cliAddr] = cli

	//新的协程给当前客户端发送消息
	go WriteMsgToClient(cli, conn)

	//广播某个在线
	message <- MakeMsg(cli, "login")

	cli.C <- MakeMsg(cli, "I am here")

	isQuit := make(chan bool)
	hasData := make(chan bool)


	//新建协程，接收用户消息
	go func() {
		buf := make([]byte, 2048)
		for {
			n, err := conn.Read(buf)
			if n == 0 {
				isQuit <- true
				fmt.Println("conn.Read err = ", err)
				return
			}

			msg := string(buf[:n-1])
			if len(msg) == 3 && msg == "who" {
				//遍历map，给当前用户发送所有成员
				conn.Write([]byte("user list:\n"))

				for _, tmp := range onlineMap {
					msg = tmp.Addr + ":" + tmp.Name + "\n"
					conn.Write([]byte(msg))
				}
			} else if len(msg) > 8 && msg[:6] == "rename" {
				//改名
				name := strings.Split(msg, "|")[1]
				cli.Name = name
				onlineMap[cliAddr] = cli
				conn.Write([]byte("rename ok\n"))

			} else {
				//转发内容
				message <- MakeMsg(cli, msg)
			}
			//代表有数据
			hasData <- true
		}
	}()

	for {
		//通过select检测channel的流动
		select {
		case <-isQuit:
			delete(onlineMap, cliAddr)            //当前用户从map移除
			message <- MakeMsg(cli, "login out") //广播谁下线了

			return
		case <-hasData:

		case <-time.After(30 * time.Second): //30s后
			delete(onlineMap, cliAddr)                     //当前用户从map移除
			message <- MakeMsg(cli, "time out leave out") //广播谁下线了
			return
		}
	}
}

func main() {
	listener, err := net.Listen("tcp", "127.0.0.1:8000")
	if err != nil {
		fmt.Println("net.Listen err = ", err)
		return
	}
	defer listener.Close()
	
	//转发消息
	go Manager()

	//主协程,阻塞等待用户连接
	for {
		conn, err := listener.Accept()
		if err !=nil {
			fmt.Println("listener.Accept err =", err)
			continue
		}

		go HandleConn(conn)	//处理用户连接
	}
}
